﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using Palsta.Core.data;
using Palsta.Core.action;

namespace Palsta.Core.exp
{
    class Expression
    {

        private static int id_count = 1;

        #region Members

        private ExpNode root;
        private int id;

        #endregion

        #region Constructors

        public Expression()
        {
            this.id = id_count++;
        }

        #endregion

        #region Accessors

        public virtual ExpNode Root
        {
            get
            {
                return this.root;
            }
            set
            {
                this.root = value;
            }
        }

        public virtual int ID
        {
            get
            {
                return this.id;
            }
        }

        #endregion

        #region Methods

        public bool Test(DataDictionary data)
        {
            if (this.root == null)
            {
                return false;
            }
            return this.root.Test(data);
        }

        public JSBlock GenerateExpJs(DataDictionary data)
        {
            JSBlock js = new JSBlock();
            js.JS = this.GetExpNodeJs(this.root, data, js.ReferenceFields);
            return js;
        }

        public void SetRootNode(ExpNode n)
        {
            this.root = n;
        }

        public ExpNode GetRootNode()
        {
            return this.root;
        }

        private String GetExpNodeJs(ExpNode node, DataDictionary data, HashSet<String> references)
        {
            switch (node.GetNodeType())
            {
                case ExpNodeType.Value:
                    ExpValueNode vn = (ExpValueNode)node;
                    return "\"" + vn.GetValue() + "\"";

                case ExpNodeType.Ref:
                    ExpRefNode rn = (ExpRefNode)node;
                    references.Add(rn.GetValueName());
                    return "getField(\"" + rn.GetValueName() + "\")";

                case ExpNodeType.Op:
                    ExpOpNode on = (ExpOpNode)node;
                    ExpNode lv = on.getLeftOperand();
                    ExpNode rv = on.getRightOperand();
                    ExpOpType ot = on.getOperator();
                    if (ot == ExpOpType.Equels)
                    {
                        return this.GetExpNodeJs(lv, data, references) + " == " + this.GetCastJs(this.GetExpNodeJs(rv, data, references), rv.GetFieldType(data), lv.GetFieldType(data));
                    }
                    else if (ot == ExpOpType.NotEquels)
                    {
                        return this.GetExpNodeJs(lv, data, references) + " != " + this.GetCastJs(this.GetExpNodeJs(rv, data, references), rv.GetFieldType(data), lv.GetFieldType(data));
                    }
                    else if (ot == ExpOpType.GreaterThan)
                    {
                        return this.GetExpNodeJs(lv, data, references) + " > " + this.GetCastJs(this.GetExpNodeJs(rv, data, references), rv.GetFieldType(data), lv.GetFieldType(data));
                    }
                    else if (ot == ExpOpType.LessThan)
                    {
                        return this.GetExpNodeJs(lv, data, references) + " < " + this.GetCastJs(this.GetExpNodeJs(rv, data, references), rv.GetFieldType(data), lv.GetFieldType(data));
                    }
                    else if (ot == ExpOpType.NotGreaterThan)
                    {
                        return this.GetExpNodeJs(lv, data, references) + " <= " + this.GetCastJs(this.GetExpNodeJs(rv, data, references), rv.GetFieldType(data), lv.GetFieldType(data));
                    }
                    else if (ot == ExpOpType.NotLessThan)
                    {
                        return this.GetExpNodeJs(lv, data, references) + " >= " + this.GetCastJs(this.GetExpNodeJs(rv, data, references), rv.GetFieldType(data), lv.GetFieldType(data));
                    }
                    return "";
                case ExpNodeType.Function:
                    ExpFunctionNode fn = (ExpFunctionNode)node;
                    String s = "xslt_" + fn.GetName() + "(";
                    ArrayList args = fn.GetArgs();
                    if (args != null)
                    {
                        for (int i = 0; i < args.Count; i++)
                        {
                            if (i > 0)
                            {
                                s = s + ",";
                            }
                            s = s + this.GetExpNodeJs((ExpNode)args[i], data, references);
                        }
                    }
                    s += ")";
                    return s;

                case ExpNodeType.Logical:
                    ExpLogicalNode logicalNode = (ExpLogicalNode)node;
                    ExpNode llv = logicalNode.getLeftOperand();
                    ExpNode lrv = logicalNode.getRightOperand();
                    ExpLogicalNode.ExpLogicalType lt = logicalNode.getOperator();
                    if (lt == ExpLogicalNode.ExpLogicalType.And)
                    {
                        return "(" + this.GetExpNodeJs(llv, data, references) + ") && (" + this.GetExpNodeJs(lrv, data, references) + ")";
                    }
                    else if (lt == ExpLogicalNode.ExpLogicalType.Or)
                    {
                        return "(" + this.GetExpNodeJs(llv, data, references) + ") || (" + this.GetExpNodeJs(lrv, data, references) + ")";
                    }
                    return "";
            }
            return "";
        }

        private String GetCastJs(String operand, FieldType fromType, FieldType toType)
        {
            if (fromType == toType)
            {
                return operand;
            }

            if (fromType == FieldType.String)
            {
                if (toType == FieldType.Boolean)
                {
                    return "castStringToBoolean(" + operand + ")";
                }
            }
            else if (fromType == FieldType.Boolean)
            {
                if (toType == FieldType.String)
                {
                    return "castBooleanToString(" + operand + ")";
                }
            }

            return operand;
        }

        #endregion

        #region Override Operators

        #endregion

        #region Static Methods

        #endregion

    }
}
